{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 多态\n",
    "<font color='red' size=4> ** 多态指的是一个事物有多种形态 **  \n",
    "    动物有多种形态：人、狗、猪 </font>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "import abc\n",
    "class Animal(metaclass=abc.ABCMeta):  # 同一类事物：动物\n",
    "    @abc.abstractmethod\n",
    "    def talk(self):\n",
    "        pass\n",
    "\n",
    "class People(Animal):  # 动物的形态之一：人\n",
    "    def talk(self):\n",
    "        print('say hello')\n",
    "\n",
    "class Dog(Animal): #动物的形态之二:狗\n",
    "    def talk(self):\n",
    "        print('say wangwang')\n",
    "\n",
    "class Pig(Animal): #动物的形态之三:猪\n",
    "    def talk(self):\n",
    "        print('say aoao')\n",
    "\n",
    "class File(metaclass=abc.ABCMeta): #同一类事物:文件\n",
    "    @abc.abstractmethod\n",
    "    def click(self):\n",
    "        pass\n",
    "\n",
    "class Text(File): #文件的形态之一:文本文件\n",
    "    def click(self):\n",
    "        print('open file')\n",
    "\n",
    "class ExeFile(File): #文件的形态之二:可执行文件\n",
    "    def click(self):\n",
    "        print('execute file')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 多态性\n",
    "<font color='blue' size=4> 1. 什么是多态动态绑定（在继承的背景下使用时，有时也称为多态性） </font> <br>\n",
    "多态性是指在不考虑实例类型的情况下使用实例：  \n",
    "在面向对象方法中一般是这样表述多态性：\n",
    "向不同的对象发送同一条消息（！！！obj.func():是调用了obj的方法func，又称为向obj发送了一条消息func），不同的对象在接收时会产生不同的行为（即方法）。\n",
    "也就是说，每个对象可以用自己的方式去响应共同的消息。所谓消息，就是调用函数，不同的行为就是指不同的实现，即执行不同的函数。\n",
    "\n",
    "比如：老师.下课铃响了（），学生.下课铃响了()，老师执行的是下班操作，学生执行的是放学操作，虽然二者消息一样，但是执行的效果不同  \n",
    "### 多态性的python演示"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "say hello\n",
      "say wangwang\n",
      "say aoao\n"
     ]
    }
   ],
   "source": [
    "peo = People()\n",
    "dog = Dog()\n",
    "pig = Pig()\n",
    "#peo、dog、pig都是动物,只要是动物肯定有talk方法\n",
    "#于是我们可以不用考虑它们三者的具体是什么类型,而直接使用\n",
    "peo.talk()\n",
    "dog.talk()\n",
    "pig.talk()\n",
    "#更进一步,我们可以定义一个统一的接口来使用\n",
    "def func(obj):\n",
    "    obj.talk()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 鸭子类型\n",
    "逗比时刻： \n",
    "<font color='red' size=4> ** Python崇尚鸭子类型，即‘如果看起来像、叫声像而且走起路来像鸭子，那么它就是鸭子’** </font>  \n",
    "python程序员通常根据这种行为来编写程序。例如，如果想编写现有对象的自定义版本，可以继承该对象、也可以创建一个外观和行为像，但与它无任何关系的全新对象，后者通常用于保存程序组件的松耦合度。\n",
    "  \n",
    "例1：利用标准库中定义的各种‘与文件类似’的对象，尽管这些对象的工作方式像文件，但他们没有继承内置文件对象的方法\n",
    "\n",
    "例2：序列类型有多种形态：字符串，列表，元组，但他们直接没有直接的继承关系"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 封装\n",
    "** 封装：隐藏对象的属性的实现细节，仅对外提供公共访问方式。  \n",
    "好处：  **\n",
    "1. 将变化隔离\n",
    "2. 便于使用\n",
    "3. 提高复用性\n",
    "4. 提高安全性 \n",
    "\n",
    "** 封装原则 **  \n",
    "1. 将不需要对外提供的内容都隐藏起来；\n",
    "2. 把属性都隐藏，提供公共方法对其访问。\n",
    "\n",
    "## 私有变量和私有方法\n",
    "** 在python中用双下划线开头的方式将属性隐藏起来（设置成私有的）**\n",
    "### 私有变量"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "#其实这仅仅这是一种变形操作\n",
    "#类中所有双下划线开头的名称如__x都会自动变形成：_类名__x的形式：\n",
    "\n",
    "class A:\n",
    "    __N=0  #类的数据属性就应该是共享的,但是语法上是可以把类的数据属性设置成私有的如__N,会变形为_A__N\n",
    "    def __init__(self):\n",
    "        self.__X = 10  # 变形为self._A__X\n",
    "    def __foo(self):   # 变形为_A__foo\n",
    "        print('from A')\n",
    "    def bar(self):\n",
    "        self.__foo()   #只有在类内部才可以通过__foo的形式访问到.\n",
    "#A._A__N是可以访问到的，即这种操作并不是严格意义上的限制外部访问，仅仅只是一种语法意义上的变形"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "** 这种自动变形的特点： **\n",
    "1. 类中定义的__x只能在内部使用，如self.__x，引用的就是变形的结果。\n",
    "\n",
    "2. 这种变形其实正是针对外部的变形，在外部是无法通过__x这个名字访问到的。\n",
    "\n",
    "3. 在子类定义的__x不会覆盖在父类定义的__x，因为子类中变形成了：_子类名__x,而父类中变形成了：_父类名__x，即双下滑线开头的属性在继承给子类时，子类是无法覆盖的。 \n",
    "\n",
    "** 这种变形需注意的问题是：**\n",
    "1. 这种机制也并没有真正意义上限制我们从外部直接访问属性，知道了类名和属性名就可以拼出名字：_类名__属性，然后就可以访问了，如a._A__N\n",
    "\n",
    "2. 变形的过程只在类的内部生效,在定义后的赋值操作，不会变形\n",
    "\n",
    "### 私有方法\n",
    "3. 在继承中，父类如果不想让子类覆盖自己的方法，可以将方法定义为私有的"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "from A\n"
     ]
    }
   ],
   "source": [
    "#把fa定义成私有的，即__fa\n",
    "class A:\n",
    "    def __fa(self):   # 在定义时就变形为_A_fa\n",
    "        print('from A')\n",
    "    def test(self):\n",
    "        self.__fa()\n",
    "\n",
    "class B(A):\n",
    "    def __fa(self):\n",
    "        print('from B')\n",
    "        \n",
    "b = B()\n",
    "b.test()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 封装与扩展性\n",
    "封装在于明确区分内外，使得类实现者可以修改封装内的东西而不影响外部调用者的代码；而外部使用用者只知道一个接口(函数)，只要接口（函数）名、参数不变，使用者的代码永远无需改变。这就提供一个良好的合作基础——或者说，只要接口这个基础约定不变，则代码改变不足为虑。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "8000"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "#类的设计者\n",
    "class Room0:\n",
    "    def __init__(self,name,owner,width,length,high):\n",
    "        self.name=name\n",
    "        self.owner=owner\n",
    "        self.__width=width\n",
    "        self.__length=length\n",
    "        self.__high=high\n",
    "    def tell_area(self):  #对外提供的接口，隐藏了内部的实现细节，此时我们想求的是面积\n",
    "        return self.__width * self.__length\n",
    "\n",
    "# 使用者\n",
    "r1 = Room0('卧室','egon',20,20,20)\n",
    "r1.tell_area()\n",
    "\n",
    "#类的设计者，轻松的扩展了功能，而类的使用者完全不需要改变自己的代码\n",
    "class Room:\n",
    "    def __init__(self,name,owner,width,length,high):\n",
    "        self.name=name\n",
    "        self.owner=owner\n",
    "        self.__width=width\n",
    "        self.__length=length\n",
    "        self.__high=high\n",
    "    def tell_area(self): #对外提供的接口，隐藏内部实现，此时我们想求的是体积,内部逻辑变了,只需求修该下列一行就可以很简答的实现,而且外部调用感知不到,仍然使用该方法，但是功能已经变了\n",
    "        return self.__width * self.__length * self.__high\n",
    "r2 = Room('卧室','egon',20,20,20)\n",
    "r2.tell_area()\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## property属性\n",
    "\n",
    "<font color='blue' size=4> ** property是一种特殊的属性，访问它时会执行一段功能（函数）然后返回值 ** </font>\n",
    "\n",
    "** 为什么要用property？ **  \n",
    "将一个类的函数定义成特性以后，对象再去使用的时候obj.name,根本无法察觉自己的name是执行了一个函数然后计算出来的，这种特性的使用方式遵循了** 统一访问 的原则 **  \n",
    "对象的封装三种方式：  \n",
    "1. public\n",
    "2. protected\n",
    "3. private  \n",
    "\n",
    "python并没有在语法上把它们三个内建到自己的class机制中，在C++里一般会将所有的所有的数据都设置为私有的，然后提供set和get方法（接口）去设置和获取，在python中通过property方法可以实现  \n",
    "<font color='blue' size=4> ** 一个静态属性property本质就是实现了get，set，delete三种方法 ** </font>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "21.913805697589478\n"
     ]
    }
   ],
   "source": [
    "class People:\n",
    "    def __init__(self,name,weight,height):\n",
    "        self.name = name\n",
    "        self.weight = weight\n",
    "        self.height = height\n",
    "    @property\n",
    "    def bmi(self):\n",
    "        return self.weight / (self.height**2)\n",
    "\n",
    "p1 = People('egon',75,1.85)\n",
    "print(p1.bmi)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "10\n",
      "314.1592653589793\n",
      "62.83185307179586\n"
     ]
    },
    {
     "ename": "AttributeError",
     "evalue": "can't set attribute",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mAttributeError\u001b[0m                            Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-15-528d92410110>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m()\u001b[0m\n\u001b[0;32m     14\u001b[0m \u001b[0mprint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mc\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0marea\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m     15\u001b[0m \u001b[0mprint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mc\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mperimeter\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 16\u001b[1;33m \u001b[0mc\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0marea\u001b[0m \u001b[1;33m=\u001b[0m \u001b[1;36m3\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[1;31mAttributeError\u001b[0m: can't set attribute"
     ]
    }
   ],
   "source": [
    "import math\n",
    "class Circle:\n",
    "    def __init__(self,radius):\n",
    "        self.radius = radius\n",
    "    @property\n",
    "    def area(self):\n",
    "        return math.pi * self.radius**2\n",
    "    @property\n",
    "    def perimeter(self):\n",
    "        return 2 * math.pi * self.radius\n",
    "\n",
    "c = Circle(10)\n",
    "print(c.radius)\n",
    "print(c.area)\n",
    "print(c.perimeter)\n",
    "c.area = 3\n",
    "'''\n",
    "抛出异常:\n",
    "AttributeError: can't set attribute\n",
    "'''"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "get的时候运行我啊\n",
      "set的时候运行我啊\n",
      "delete的时候运行我啊\n"
     ]
    }
   ],
   "source": [
    "class Foo:\n",
    "    @property\n",
    "    def AAA(self):\n",
    "        print('get的时候运行我啊')\n",
    "\n",
    "    @AAA.setter\n",
    "    def AAA(self,value):\n",
    "        print('set的时候运行我啊')\n",
    "\n",
    "    @AAA.deleter\n",
    "    def AAA(self):\n",
    "        print('delete的时候运行我啊')\n",
    "\n",
    "#只有在属性AAA定义property后才能定义AAA.setter,AAA.deleter\n",
    "f1=Foo()\n",
    "f1.AAA         # get方法\n",
    "f1.AAA='aaa'   # set方法\n",
    "del f1.AAA     # delete方法"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "get的时候运行我啊\n",
      "set的时候运行我啊\n",
      "delete的时候运行我啊\n"
     ]
    }
   ],
   "source": [
    "class Foo1:\n",
    "    def get_AAA(self):\n",
    "        print('get的时候运行我啊')\n",
    "\n",
    "    def set_AAA(self,value):\n",
    "        print('set的时候运行我啊')\n",
    "\n",
    "    def delete_AAA(self):\n",
    "        print('delete的时候运行我啊')\n",
    "    AAA=property(get_AAA,set_AAA,delete_AAA) #内置property三个参数与get,set,delete一一对应\n",
    "\n",
    "f1=Foo1()\n",
    "f1.AAA\n",
    "f1.AAA='aaa'\n",
    "del f1.AAA"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## classmethod"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "dog\n"
     ]
    }
   ],
   "source": [
    "class Classmethod_Demo():\n",
    "    role = 'dog'\n",
    "    @classmethod\n",
    "    def func(cls):\n",
    "        print(cls.role)\n",
    "\n",
    "Classmethod_Demo.func()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## staticmethod"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "当普通方法用\n"
     ]
    }
   ],
   "source": [
    "class Staticmethod_Demo():\n",
    "    role = 'dog'\n",
    "\n",
    "    @staticmethod\n",
    "    def func():\n",
    "        print(\"当普通方法用\")\n",
    "\n",
    "Staticmethod_Demo.func()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "in son\n",
      "CHINA\n",
      "CHINA\n",
      "CHINA\n"
     ]
    }
   ],
   "source": [
    "class Foo:\n",
    "    def func(self):\n",
    "        print('in father')\n",
    "\n",
    "\n",
    "class Son(Foo):\n",
    "    def func(self):\n",
    "        print('in son')\n",
    "\n",
    "s = Son()\n",
    "s.func()\n",
    "\n",
    "class A:\n",
    "    __role = 'CHINA'\n",
    "    @classmethod\n",
    "    def show_role(cls):\n",
    "        print(cls.__role)\n",
    "\n",
    "    @staticmethod\n",
    "    def get_role():\n",
    "        return A.__role\n",
    "\n",
    "    @property\n",
    "    def role(self):\n",
    "        return self.__role\n",
    "\n",
    "a = A()\n",
    "print(a.role)\n",
    "print(a.get_role())\n",
    "a.show_role()\n",
    "# __role在类中有哪些身份？\n",
    "# 以上代码分别输出哪些内容？\n",
    "# 这三个装饰器分别起了什么作用？有哪些区别？"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
